Redshiftで値の途中に引用符等を含むデータを取り込む
"
の入った文字列でエラーになった
Redshiftでs3にあるCSVテキスト1ファイルからデータをCOPYする際に、データに"
(ダブルクォーテーション)が含まれているとエラーになってしまっていたので、その対処法と、COPYのパラメータについて調べてみました。
失敗した例
S3に置いてあるファイルに以下のようなレコードがあるとき、
0001|aaa "bbb" ccc
失敗していたCOPYは以下のようなものでした。
COPY quotation_test FROM 's3://xxxxxxxxx/xxxx/xxxx' ACCESS_KEY_ID '〜〜〜〜' SECRET_ACCESS_KEY '〜〜〜〜' SESSION_TOKEN '〜〜〜〜' FORMAT AS CSV DELIMITER '|' ;
FORMAT AS CSV
で、デリミタとして|
を指定しています。
失敗の原因を調べるためにSTL_LOAD_ERRORSを見てみると
select * from stl_load_errors; ==> Invalid quote formatting for CSV
と出ていました。
どうやら"
が原因のようです。
失敗した原因
デリミタは|
に設定しているので、"
はなんら影響を及ぼさないように見えますが、実はFORMAT AS CSV
を指定している時にはQUOTE
というパラメータのデフォルト値として"
が使われています。
QUOTE
パラメータとは
QUOTE
パラメータはFORMAT AS CSV
が設定された時に同時に設定されるパラメータで、デフォルト値は"
です。
QUOTE
として"
が設定されている時の挙動は以下のようになります。
- 各カラムの値が
"
で囲まれている場合、それを除去する - カラムの値を囲う用途以外の
"
が発見されたらエラーとする
この2番目がまさに今回のエラーの原因となっていました。
QUOTE
に設定された文字は、値を囲うための専用文字として確保されてしまうようです。
なお、ややこしいのですが、 値が"
で囲まれていない分には全く構わない ので、全ての値を囲う必要はありません。2
対処法1:元データを編集
FORMAT AS CSV
を指定した時にも、"
をエスケープする方法があるので、それを使います。
- その値全体を
"
で囲う ""
(ダブルクォーテーション2つ)のように、QUOTE
文字を2つ並べる
とすればOKです。
ポイントは、値の中に"
を登場させたい場合は、 必ず、その値全体が"
で囲われていなければならない という点です。
ただ""
に直しても相変わらずエラーになってしまうので注意が必要です。
元データを編集してCOPY可能にした例
0001|"aaa ""bbb"" ccc"
COPY quotation_test FROM 's3://xxxxxxxxx/xxxx/xxxx' ACCESS_KEY_ID '〜〜〜〜' SECRET_ACCESS_KEY '〜〜〜〜' SESSION_TOKEN '〜〜〜〜' FORMAT AS CSV DELIMITER '|' ;
id | message |
---|---|
0001 | aaa "bbb" ccc |
これで無事COPYが成功します。
対処法2:COPYパラメータで対処
上記では元データを編集しましたが、現実には元データは編集できなかったり、もう大量にデータが溜まっていてそれを全て修正するのは大変(手間というより、データを書き換えてしまう恐れなど)なことがほとんどかと思います。 なので、COPYのパラメータを修正してこのデータに対処します。
FORMAT
を指定しない
実は対処法はとても簡単で、FORMAT AS CSV
の指定をなくせば大丈夫です。
"
に特別な意味を与えていたQUOTE
パラメータの設定は、FORMAT AS CSV
に付随して有効になったものですので、それを解除します。
FORMAT
を指定しない場合の挙動は以下です。
DELIMITER
文字をカラム区切りとするDELIMITER
文字のデフォルト値は|
デリミタ文字を見つけて、そこで分割するだけ。非常にシンプルです。
COPYのパラメータを編集してCOPY可能にした例
0001|aaa "bbb" ccc
COPY quotation_test FROM 's3://xxxxxxxxx/xxxx/xxxx' ACCESS_KEY_ID '〜〜〜〜' SECRET_ACCESS_KEY '〜〜〜〜' SESSION_TOKEN '〜〜〜〜' DELIMITER '|' --デフォルト値なので実は不要 ;
id | message |
---|---|
0001 | aaa "bbb" ccc |
単純に|
で区切っただけですので、単純明快です。
FORMAT
指定ナシに出来ないか考えよう
FORMAT AS CSV
を指定するとQUOTE
が設定され、上記のようなハマりポイントになりがちです。
しかし、QUOTE
の仕様が少し分かりづらいながらも、FORMAT AS CSV
が指定されているケースは少なくないように思います。
その原因は「FORMAT
に何か指定しないといけないのではないか?」というのが主な原因だと思うのですが、いかがでしょうか?
もし「デリミタを指定して、それでカラムを区切りたいだけ」と考えているなら、実は「FORMAT
を指定せずDELMIITER
を指定する」が正解です。
CSVという名前がいかにもそれっぽいのですが、QUOTE
のことを忘れると痛い目を見ます。
もちろん、QUOTE
が有効になることで想定外データを検知しやすくなるなどの側面もあると思いますので、使い勝手とはトレードオフですが、何にしてもCOPYのパラメータなどの仕様はしっかり理解しておくに越したことはありません。
まとめ
RedshiftのCOPYでFORMAT AS CSV
を指定している時に、"
でエラーになってしまうことを起点に、QUOTE
パタメータの挙動と、FORMAT
を指定しないときの挙動を説明しました。
「デリミタを指定してテキストファイルを読みたい」というだけなら、FORMAT
を指定しなくても良いというのは、私としては盲点だったので勉強になりました。
誰かのご参考になれば幸いです。